package cn.jingyuan.swan.cloud.web;

import cn.jingyuan.bee.exception.SystemException;
import cn.jingyuan.swan.cloud.core.ExCode;
import cn.jingyuan.swan.cloud.core.Result;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingPathVariableException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.context.request.async.AsyncRequestTimeoutException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.util.Objects;

/**
 * 异常解析工具
 */
public class ExceptionResolverUtils {

    /**
     * 解析异常
     *
     * @param ex 异常
     */
    public static <D> Result<D> resolveEx(Exception ex) {
        Result<D> result = resolveExSystem(ex);

        if (Objects.nonNull(result)) {
            return result;
        }

        result = resolveExWeb(ex);
        if (Objects.nonNull(result)) {
            return result;
        }

        ExCode exCode = ExCode.INTERNAL_SERVER_ERROR;
        Integer httpStatus = HttpStatus.INTERNAL_SERVER_ERROR.value();

        return new Result<D>()
            .setCode(exCode.getCode())
            .setCodeMsg(exCode.getCodeMsg())
            .setHttpStatus(httpStatus);
    }

    /**
     * 解析 SystemException 及其子类异常
     * <pre>
     *     1、request or response 可以为 {@code null}
     * </pre>
     *
     * @param ex {@link Exception} 系统异常
     *
     * @return {@code null} 如果 ex is not instanceof SystemException 为 {@code null}
     */
    public static <D> Result resolveExSystem(Exception ex) {
        if (!(ex instanceof SystemException)) {
            return null;
        }

        SystemException sysEx = (SystemException) ex;

        String code = sysEx.getCode();
        String codeMsg = sysEx.getCodeMsg();

        HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;

        // 1005 为拒绝访问异常 --> ExCode.ACCESS_XXX
        if (code.startsWith("1005")) {
            httpStatus = HttpStatus.FORBIDDEN;
        }


        return new Result<D>()
            .setCode(code)
            .setCodeMsg(codeMsg)
            .setHttpStatus(httpStatus.value());
    }

    /**
     * 解析  Web（Servlet 及 Mcv） 异常
     * <pre>
     *     1、request or response 可以为 {@code null}
     * </pre>
     *
     * @param ex Servlet 及 Mcv 异常
     *
     * @return {@code null} 如果 ex 为 {@code null}
     */
    public static <D> Result<D> resolveExWeb(Exception ex) {
        if (Objects.isNull(ex)) {
            return null;
        }

        // ex 400
        if (ex instanceof ServletRequestBindingException ||
            // ex instanceof TypeMismatchException ||
            ex instanceof HttpMessageNotReadableException ||
            ex instanceof MethodArgumentNotValidException ||
            ex instanceof MissingServletRequestPartException ||
            ex instanceof BindException
        ) {
            return buildResult(ExCode.BAD_REQUEST, HttpStatus.BAD_REQUEST);
        }

        // ex 404
        if (ex instanceof NoHandlerFoundException) {
            return buildResult(ExCode.NOT_FOUND, HttpStatus.NOT_FOUND);
        }

        // ex 405
        if (ex instanceof HttpRequestMethodNotSupportedException) {
            return buildResult(ExCode.METHOD_NOT_ALLOWED, HttpStatus.METHOD_NOT_ALLOWED);
        }

        // ex 406
        if (ex instanceof HttpMediaTypeNotAcceptableException) {
            return buildResult(ExCode.NOT_ACCEPTABLE, HttpStatus.NOT_ACCEPTABLE);
        }

        // ex 415
        if (ex instanceof HttpMediaTypeNotSupportedException) {
            return buildResult(ExCode.UNSUPPORTED_MEDIA_TYPE, HttpStatus.UNSUPPORTED_MEDIA_TYPE);
        }

        // ex 500
        if (ex instanceof ConversionNotSupportedException ||
            ex instanceof HttpMessageNotWritableException ||
            ex instanceof MissingPathVariableException
        ) {
            return buildResult(ExCode.INTERNAL_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR);
        }

        // ex 503
        if (ex instanceof AsyncRequestTimeoutException) {
            return buildResult(ExCode.INTERNAL_SERVER_ERROR, HttpStatus.SERVICE_UNAVAILABLE);
        }

        return null;
    }

    public static <D> Result<D> buildResult(ExCode exCode, HttpStatus status) {
        return new Result<D>()
            .setCode(exCode.getCode())
            .setCodeMsg(exCode.getCodeMsg())
            .setHttpStatus(status.value());
    }

}
